{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "a8b892c8",
   "metadata": {},
   "source": [
    "Printed and electronic copies of *Modeling and Simulation in Python* are available from [No Starch Press](https://nostarch.com/modeling-and-simulation-python) and [Bookshop.org](https://bookshop.org/p/books/modeling-and-simulation-in-python-allen-b-downey/17836697?ean=9781718502161) and [Amazon](https://amzn.to/3y9UxNb)."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "creative-motel",
   "metadata": {},
   "source": [
    "# Proportional Growth"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "imported-table",
   "metadata": {
    "tags": []
   },
   "source": [
    "*Modeling and Simulation in Python*\n",
    "\n",
    "Copyright 2021 Allen Downey\n",
    "\n",
    "License: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "electoral-turkey",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# install Pint if necessary\n",
    "\n",
    "try:\n",
    "    import pint\n",
    "except ImportError:\n",
    "    !pip install pint"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "formal-context",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# download modsim.py if necessary\n",
    "\n",
    "from os.path import basename, exists\n",
    "\n",
    "def download(url):\n",
    "    filename = basename(url)\n",
    "    if not exists(filename):\n",
    "        from urllib.request import urlretrieve\n",
    "        local, _ = urlretrieve(url, filename)\n",
    "        print('Downloaded ' + local)\n",
    "    \n",
    "download('https://raw.githubusercontent.com/AllenDowney/' +\n",
    "         'ModSimPy/master/modsim.py')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "progressive-typing",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# import functions from modsim\n",
    "\n",
    "from modsim import *"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "375794f9",
   "metadata": {},
   "source": [
    "This chapter is available as a Jupyter notebook where you can read the text, run the code, and work on the exercises. \n",
    "Click here to access the notebooks: <https://allendowney.github.io/ModSimPy/>."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "amber-contrary",
   "metadata": {
    "tags": []
   },
   "source": [
    "Here's the data from the previous chapter again."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "critical-addition",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "download('https://raw.githubusercontent.com/AllenDowney/' +\n",
    "         'ModSimPy/master/data/World_population_estimates.html')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "naughty-swing",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from pandas import read_html\n",
    "\n",
    "filename = 'World_population_estimates.html'\n",
    "tables = read_html(filename, header=0, index_col=0, decimal='M')\n",
    "table2 = tables[2]\n",
    "table2.columns = ['census', 'prb', 'un', 'maddison', \n",
    "                  'hyde', 'tanton', 'biraben', 'mj', \n",
    "                  'thomlinson', 'durand', 'clark']"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "quality-spectrum",
   "metadata": {},
   "source": [
    "In the previous chapter we simulated a model of world population with\n",
    "constant growth. In this chapter we'll see if we can make a better model\n",
    "with growth proportional to the population.\n",
    "\n",
    "But first, we'll improve the code from the previous chapter by\n",
    "encapsulating it in a function and adding a new feature, a `System` object."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "therapeutic-merchant",
   "metadata": {},
   "source": [
    "## System Objects\n",
    "\n",
    "Like a `State` object, a `System` object contains variables and their\n",
    "values. The difference is:\n",
    "\n",
    "-   `State` objects contain state variables that get updated in the course of a simulation.\n",
    "\n",
    "-   `System` objects contain *system parameters*, which usually don't get updated over the course of a simulation.\n",
    "\n",
    "For example, in the bike share model, state variables include the number of bikes at each location, which get updated whenever a customer moves a bike. System parameters include the number of locations, total number of bikes, and arrival rates at each location.\n",
    "\n",
    "In the population model, the only state variable is the population.\n",
    "System parameters include the annual growth rate, the initial population, and the start and end times.\n",
    "\n",
    "Suppose we have the following variables, as computed in the previous\n",
    "chapter (assuming `table2` is the `DataFrame` we read from the file):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "numerous-university",
   "metadata": {},
   "outputs": [],
   "source": [
    "un = table2.un / 1e9\n",
    "census = table2.census / 1e9\n",
    "\n",
    "t_0 = census.index[0]\n",
    "t_end = census.index[-1]\n",
    "elapsed_time = t_end - t_0\n",
    "\n",
    "p_0 = census[t_0]\n",
    "p_end = census[t_end]\n",
    "\n",
    "total_growth = p_end - p_0\n",
    "annual_growth = total_growth / elapsed_time"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "starting-cooling",
   "metadata": {},
   "source": [
    "Some of these are parameters we need to simulate the system; others are temporary values we can discard. \n",
    "To distinguish between them, we'll put the parameters we need in a `System` object like this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "colonial-domestic",
   "metadata": {},
   "outputs": [],
   "source": [
    "system = System(t_0=t_0, \n",
    "                t_end=t_end,\n",
    "                p_0=p_0,\n",
    "                annual_growth=annual_growth)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fleet-beaver",
   "metadata": {},
   "source": [
    "`t0` and `t_end` are the first and last years; `p_0` is the initial\n",
    "population, and `annual_growth` is the estimated annual growth.\n",
    "\n",
    "The assignment `t_0=t_0` reads the value of the existing variable named `t_0`, which we created previously, and stores it in a new system variable, also named `t_0`.\n",
    "The variables inside the `System` object are distinct from other variables, so you can change one without affecting the other, even if they have the same name.\n",
    "\n",
    "So this `System` object contains four new variables; here's what they look like."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "floral-routine",
   "metadata": {},
   "outputs": [],
   "source": [
    "show(system)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "combined-banking",
   "metadata": {},
   "source": [
    "Next we'll wrap the code from the previous chapter in a function:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "pacific-challenge",
   "metadata": {},
   "outputs": [],
   "source": [
    "def run_simulation1(system):\n",
    "    results = TimeSeries()\n",
    "    results[system.t_0] = system.p_0\n",
    "    \n",
    "    for t in range(system.t_0, system.t_end):\n",
    "        results[t+1] = results[t] + system.annual_growth\n",
    "    \n",
    "    return results"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "rough-strain",
   "metadata": {},
   "source": [
    "`run_simulation1` takes a `System` object and reads from it the values of `t_0`, `t_end`, and `annual_growth`.\n",
    "\n",
    "It simulates population growth over time and returns the results in a `TimeSeries`.\n",
    "Here's how we call it."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "electoral-breach",
   "metadata": {},
   "outputs": [],
   "source": [
    "results1 = run_simulation1(system)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ordinary-sound",
   "metadata": {},
   "source": [
    "Here's the function we used in the previous chapter to plot the estimates."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "peripheral-cassette",
   "metadata": {},
   "outputs": [],
   "source": [
    "def plot_estimates():\n",
    "    census.plot(style=':', label='US Census')\n",
    "    un.plot(style='--', label='UN DESA')\n",
    "    decorate(xlabel='Year', \n",
    "             ylabel='World population (billion)') "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "warming-audience",
   "metadata": {},
   "source": [
    "And here are the results."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "capable-diana",
   "metadata": {},
   "outputs": [],
   "source": [
    "results1.plot(label='model', color='gray')\n",
    "plot_estimates()\n",
    "decorate(title='Constant growth model')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "exposed-witness",
   "metadata": {},
   "source": [
    "It might not be obvious that using functions and `System` objects is a\n",
    "big improvement, and for a simple model that we run only once, maybe\n",
    "it's not. But as we work with more complex models, and when we run many simulations with different parameters, we'll see that this way of organizing the code makes a big difference.\n",
    "\n",
    "Now let's see if we can improve the model."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "geographic-hormone",
   "metadata": {},
   "source": [
    "## Proportional Growth Model\n",
    "\n",
    "The biggest problem with the constant growth model is that it doesn't\n",
    "make any sense. It is hard to imagine how people all over the world\n",
    "could conspire to keep population growth constant from year to year.\n",
    "\n",
    "On the other hand, if some fraction of the population dies each year,\n",
    "and some fraction gives birth, we can compute the net change in the\n",
    "population like this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "laughing-wesley",
   "metadata": {},
   "outputs": [],
   "source": [
    "def run_simulation2(system):\n",
    "    results = TimeSeries()\n",
    "    results[system.t_0] = system.p_0\n",
    "    \n",
    "    for t in range(system.t_0, system.t_end):\n",
    "        births = system.birth_rate * results[t]\n",
    "        deaths = system.death_rate * results[t]\n",
    "        results[t+1] = results[t] + births - deaths\n",
    "        \n",
    "    return results"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "educated-portugal",
   "metadata": {},
   "source": [
    "Each time through the loop, we use the parameter `birth_rate` to compute the number of births, and `death_rate` to compute the number of deaths.\n",
    "The rest of the function is the same as `run_simulation1`.\n",
    "\n",
    "Now we can choose the values of `birth_rate` and `death_rate` that best fit the data. \n",
    "For the death rate, I'll use 7.7 deaths per 1000 people, which was roughly the global death rate in 2020 (see <https://www.indexmundi.com/world/death_rate.html>).\n",
    "I chose the birth rate by hand to fit the population data."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "wired-brief",
   "metadata": {},
   "outputs": [],
   "source": [
    "system.death_rate = 7.7 / 1000\n",
    "system.birth_rate = 25 / 1000"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "sufficient-contest",
   "metadata": {},
   "source": [
    "Then I ran the simulation and plotted the results:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "looking-trace",
   "metadata": {},
   "outputs": [],
   "source": [
    "results2 = run_simulation2(system)\n",
    "results2.plot(label='model', color='gray')\n",
    "plot_estimates()\n",
    "decorate(title='Proportional growth model')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "suited-costs",
   "metadata": {},
   "source": [
    "The proportional model fits\n",
    "the data well from 1950 to 1965, but not so well after that. Overall,\n",
    "the *quality of fit* is not as good as the constant growth model,\n",
    "which is surprising, because it seems like the proportional model is\n",
    "more realistic.\n",
    "\n",
    "In the next chapter we'll try one more time to find a model that makes\n",
    "sense and fits the data. But first, I want to make a few more\n",
    "improvements to the code."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "appropriate-checkout",
   "metadata": {},
   "source": [
    "## Factoring Out the Update Function\n",
    "\n",
    "`run_simulation1` and `run_simulation2` are nearly identical except for the body of the `for` loop, where we compute the population for the next year.\n",
    "\n",
    "Rather than repeat identical code, we can separate the things that\n",
    "change from the things that don't. First, I'll pull out the births and deaths from `run_simulation2` and make a function:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "handmade-permit",
   "metadata": {},
   "outputs": [],
   "source": [
    "def growth_func1(t, pop, system):\n",
    "    births = system.birth_rate * pop\n",
    "    deaths = system.death_rate * pop\n",
    "    return births - deaths"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fabulous-bankruptcy",
   "metadata": {},
   "source": [
    "`growth_func1` takes as arguments the current year, current population, and a `System` object; it returns the net population growth during the current year.\n",
    "\n",
    "This function does not use `t`, so we could leave it out. But we will see other growth functions that need it, and it is convenient if they all take the same parameters, used or not.\n",
    "Now we can write a function that runs any model:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "civilian-accused",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "def run_simulation(system, growth_func):\n",
    "    results = TimeSeries()\n",
    "    results[system.t_0] = system.p_0\n",
    "    \n",
    "    for t in range(system.t_0, system.t_end):\n",
    "        growth = growth_func(t, results[t], system)\n",
    "        results[t+1] = results[t] + growth\n",
    "        \n",
    "    return results"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "failing-assist",
   "metadata": {},
   "source": [
    "This function demonstrates a feature we have not seen before: it takes a\n",
    "function as a parameter! When we call `run_simulation`, the second\n",
    "parameter is a function, like `growth_func1`, that computes the\n",
    "population for the next year.\n",
    "\n",
    "Here's how we call it:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "wicked-seeking",
   "metadata": {},
   "outputs": [],
   "source": [
    "results = run_simulation(system, growth_func1)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "simple-camel",
   "metadata": {},
   "source": [
    "Passing a function as an argument is the same as passing any other\n",
    "value. The argument, which is `growth_func1` in this example, gets\n",
    "assigned to the parameter, which is called `growth_func`. Inside\n",
    "`run_simulation`, we can call `growth_func` just like any other function.\n",
    "\n",
    "Each time through the loop, `run_simulation` calls `growth_func1` to compute net growth, and uses it to compute the population during the next year."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "spectacular-paradise",
   "metadata": {},
   "source": [
    "## Combining Birth and Death\n",
    "\n",
    "We can simplify the code slightly by combining births and deaths to compute the net growth rate. \n",
    "Instead of two parameters, `birth_rate` and `death_rate`, we can write the update function in terms of a single parameter that represents the difference:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "impressive-model",
   "metadata": {},
   "outputs": [],
   "source": [
    "system.alpha = system.birth_rate - system.death_rate"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "modern-uncertainty",
   "metadata": {},
   "source": [
    "The name of this parameter, `alpha`, is the conventional name for a\n",
    "proportional growth rate.\n",
    "\n",
    "Here's the modified version of `growth_func1`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "familiar-helena",
   "metadata": {},
   "outputs": [],
   "source": [
    "def growth_func2(t, pop, system):\n",
    "    return system.alpha * pop"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "understanding-typing",
   "metadata": {},
   "source": [
    "And here's how we run it:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "independent-effectiveness",
   "metadata": {},
   "outputs": [],
   "source": [
    "results = run_simulation(system, growth_func2)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "everyday-delicious",
   "metadata": {},
   "source": [
    "The results are the same as the previous versions, but now the code is organized in a way that makes it easy to explore other models."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "attractive-steps",
   "metadata": {},
   "source": [
    "## Summary\n",
    "\n",
    "In this chapter, we wrapped the code from the previous chapter in functions and used a `System` object to store the parameters of the system.\n",
    "\n",
    "We explored a new model of population growth, where the number of births and deaths is proportional to the current population.  This model seems more realistic, but it turns out not to fit the data particularly well.\n",
    "\n",
    "In the next chapter, we'll try one more model, which is based on the assumption that the population can't keep growing forever.\n",
    "But first, you might want to work on some exercises."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "looking-douglas",
   "metadata": {},
   "source": [
    "## Exercises"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "compliant-preserve",
   "metadata": {},
   "source": [
    "### Exercise 1\n",
    "\n",
    " Maybe the reason the proportional model doesn't work very well is that the growth rate, `alpha`, is changing over time.  So let's try a model with different growth rates before and after 1980 (as an arbitrary choice).\n",
    "\n",
    "Write an update function that takes `t`, `pop`, and `system` as parameters.  The system object, `system`, should contain two parameters: the growth rate before 1980, `alpha1`, and the growth rate after 1980, `alpha2`.  It should use `t` to determine which growth rate to use.\n",
    "\n",
    "Test your function by calling it directly, then pass it to `run_simulation`.  Plot the results.  Adjust the parameters `alpha1` and `alpha2` to fit the data as well as you can."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "minus-recommendation",
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "# Solution goes here"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "headed-amsterdam",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Solution goes here"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "preliminary-carnival",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Solution goes here"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "adaptive-tiger",
   "metadata": {},
   "source": [
    "## Under the Hood\n",
    "\n",
    "The `System` object defined in the ModSim library, is based on the `SimpleNamespace` object defined in a standard Python library called `types`; the documentation is at <https://docs.python.org/3.7/library/types.html#types.SimpleNamespace>."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "injured-mailman",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "celltoolbar": "Tags",
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
